<?php
/**
 * Server library
 *
 * Contains all code needed to get data from RETS server.
 * @package Packages
 */

/**
 *
 */
require_once(AD_SERVER_PATH . '/pkgs/init.php');
require_once(AD_SERVER_PATH . '/pkgs/crypter.php');
require_once(AD_SERVER_PATH . '/pkgs/logger.php');
require_once(AD_SERVER_PATH . '/pkgs/utils.php');

/**
 * Server library
 *
 * Contains all code needed to get data from RETS server.
 * @package Packages
 */
class CServer
{
	private $_rets;

	/**
	 * Constructor
	 *
	 * Sets library variables.
	 */
	public function __construct()
	{
		$this->_rets = null;
	}

	/**
	 * Execute user command
	 *
	 * Reads users desired command and executes it.
	 */
	public function execute()
	{
		$sub_cmd = strtolower(CUtils::get_param('sub_cmd'));
		switch ($sub_cmd)
		{
			case 'img':
				$this->_print_img(CUtils::get_param('ml_number'), CUtils::get_param('img_index'));
				break;

			case 'lookup':
				$this->_do_lookup(FALSE, CUtils::get_param('field'));
				break;

			case 'get_disabled_flds':
				$this->_get_disabled_flds();
				break;

			case 'get_metadata_flds':
				$this->_get_metadata_fields();
				break;

			case 'get_metadata_vals':
				$this->_get_metadata_values(CUtils::get_param('field'));
				break;

			case 'search':
				$this->get_list(FALSE);
				CLogger::log('INFO', CCrypter::decrypt($_COOKIE['AD_RETS_U']), 'User performed a search.');
				break;

			case 'test':
				$this->_do_test();
				CLogger::log('INFO', CCrypter::decrypt($_COOKIE['AD_RETS_U']), 'User performed a server test.');
				break;

			default:
				require_once(AD_SERVER_PATH . '/templates/html/ad.htm');
				break;
		}
	}

	/**
	 * Initilize RETS connection
	 *
	 * Gets username and password from cookie
	 * and logs user into RETS.
	 */
	private function _init_rets()
	{
		if (null == $this->_rets)
		{
			$_user = CUtils::get_cookie('AD_RETS_U');
			$_pwd  = CUtils::get_cookie('AD_RETS_P');
			$this->_rets = CUtils::get_rets($_user, $_pwd);
		}
	}

	/**
	 * Prep string for search
	 *
	 * Preps string depending on type and returns clean string.
	 * @param string $str
	 * @param string $format (date|dollar|null)
	 * @return string $str
	 */
	private function _prep_string_for_search($str, $format)
	{
		switch ($format)
		{
			case 'date':
				$time_stamp = strtotime('-'. $str .'day');
				$str = date('Y-m-d', $time_stamp);
				break;

			case 'dollar':
				$str = str_replace(',', '', $str);
				$str = str_replace('$', '', $str);
				break;

			default:
				break;
		}
		return str_replace('@@', '+', $str);
	}

	/**
	 * Append param to query
	 *
	 * Reads search parameter and returns string
	 * to be appended to search string.
	 * @param string $param
	 * @param char $prefix
	 * @param char $suffix
	 * @param string $format (date|dollar|null)
	 * @return string $search
	 */
	private function _append_query_param($param, $prefix, $suffix, $format = '')
	{
		$search = '';
		$str = CUtils::get_param($param);
		if (null != $str)
		{
			$str = $this->_prep_string_for_search($str, $format);
			$values = explode(' ', $str);
			foreach($values AS $val)
			{
				$search .= (',(' . $GLOBALS['MAP_RESULT_STD_NAME'][$param] . '=' . $prefix . $val . $suffix . ')');
			}
		}
		return $search;
	}

	/**
	 * Append custom queries
	 *
	 * Reads in textarea with custom queries, parses them,
	 * and returns string to be appended to search string.
	 * @param string $param
	 * @return string $search
	 */
	private function _append_query_custom_param($param)
	{
		$search = '';
		$str = CUtils::get_param($param);
		if (null != $str)
		{
			$pairs = explode(',', $str);
			foreach($pairs AS $pair)
			{
				$val = explode('=', $pair);
				$search .= (',(' . strtolower($val[0]) . '=' . strtolower($val[1]) . ')');
			}
		}
		return $search;
	}

	/**
	 * Build search query
	 *
	 * Read search parameters and build search query.
	 * @return string $search
	 */
	private function _build_rets_query()
	{
		$search = AD_RETS_SEARCH;

		$str = CUtils::get_param('ml_number');
		if (null != $str)
		{
			$search .= (',('.$GLOBALS['MAP_RESULT_STD_NAME']['ml_number'].'='.$str.')');
		}
		else //
		{
			global $MAP_QUERY_PREFIX, $MAP_QUERY_SUFFIX;

			$search .= $this->_append_query_param('street_name',		$MAP_QUERY_PREFIX['street_name'],		$MAP_QUERY_SUFFIX['street_name'],		'');
			$search .= $this->_append_query_param('city',				$MAP_QUERY_PREFIX['city'],				$MAP_QUERY_SUFFIX['city'],				'');
			$search .= $this->_append_query_param('zip',				$MAP_QUERY_PREFIX['zip'],				$MAP_QUERY_SUFFIX['zip'],				'');
			$search .= $this->_append_query_param('school_district',	$MAP_QUERY_PREFIX['school_district'],	$MAP_QUERY_SUFFIX['school_district'],	'');
			$search .= $this->_append_query_param('sub_division',		$MAP_QUERY_PREFIX['sub_division'],		$MAP_QUERY_SUFFIX['sub_division'],		'');

			$search .= $this->_append_query_param('status',				$MAP_QUERY_PREFIX['status'],			$MAP_QUERY_SUFFIX['status'],			'');
			$search .= $this->_append_query_param('listing_date',		$MAP_QUERY_PREFIX['listing_date'],		$MAP_QUERY_SUFFIX['listing_date'],		'date');
			$search .= $this->_append_query_param('listing_price',		$MAP_QUERY_PREFIX['listing_price'],		$MAP_QUERY_SUFFIX['listing_price'],		'dollar');
			$search .= $this->_append_query_param('selling_date',		$MAP_QUERY_PREFIX['selling_date'],		$MAP_QUERY_SUFFIX['selling_date'],		'date');
			$search .= $this->_append_query_param('selling_price',		$MAP_QUERY_PREFIX['selling_price'],		$MAP_QUERY_SUFFIX['selling_price'],		'dollar');

			$search .= $this->_append_query_param('bed_rooms',			$MAP_QUERY_PREFIX['bed_rooms'],			$MAP_QUERY_SUFFIX['bed_rooms'],			'');
			$search .= $this->_append_query_param('bath_rooms',			$MAP_QUERY_PREFIX['bath_rooms'],		$MAP_QUERY_SUFFIX['bath_rooms'],		'');
			$search .= $this->_append_query_param('sqft_above_ground',	$MAP_QUERY_PREFIX['sqft_above_ground'],	$MAP_QUERY_SUFFIX['sqft_above_ground'],	'');
			$search .= $this->_append_query_param('garage',				$MAP_QUERY_PREFIX['garage'],			$MAP_QUERY_SUFFIX['garage'],			'');
			$search .= $this->_append_query_param('exterior',			$MAP_QUERY_PREFIX['exterior'],			$MAP_QUERY_SUFFIX['exterior'],			'');
			$search .= $this->_append_query_param('style',				$MAP_QUERY_PREFIX['style'],				$MAP_QUERY_SUFFIX['style'],				'');
			$search .= $this->_append_query_param('year_built',			$MAP_QUERY_PREFIX['year_built'],		$MAP_QUERY_SUFFIX['year_built'],		'');

			$search .= $this->_append_query_custom_param('saved_queries');
		}
		$search .= ')';

		$search = str_replace("(),", '', $search);
		return $search;
	}

	/**
	 * Get disabled fields
	 *
	 * Reads in disabled fields and outputs them in XML format.
	 */
	private function _get_disabled_flds()
	{
		$xml = '<rows>';

		if (is_array($GLOBALS['DISABLED_SEARCH_FORM_FIELDS']))
		{
			foreach ($GLOBALS['DISABLED_SEARCH_FORM_FIELDS'] AS $field)
			{
				$xml .= '<row>';
				$xml .= '<key>'   . $field . '</key>';
				$xml .= '<value>' . $field . '</value>';
				$xml .= '</row>';
			}
		}
		$xml .= '</rows>';

		$this->_send_xml(FALSE, $xml);
	}

	/**
	 * Get META data fields
	 *
	 * Reads in META data fields and outputs them in XML format.
	 */
	private function _get_metadata_fields()
	{
		$this->_init_rets();

		$xml = '<rows>';

		$values = $this->_rets->GetMetadataTable('Property', AD_RETS_SEARCH_CLASS);
		foreach ($values AS $val)
		{
			if (null != $val['LookupName'])
			{
				$xml .= '<row>';
				$xml .= '<key>'   . htmlspecialchars($val['LookupName'] . '@@' . $val['SystemName']) . '</key>';
				$xml .= '<value>' . htmlspecialchars($val['LongName']) . '</value>';
				$xml .= '</row>';
			}
		}
		$xml .= '</rows>';

		$this->_send_xml(FALSE, $xml);
	}

	/**
	 * Get META data values
	 *
	 * Reads in META data field values and outputs them in XML format.
	 * @param string $lookup
	 */
	private function _get_metadata_values($lookup)
	{
		$this->_init_rets();

		$xml = '<rows>';

		$fields = explode('@@', $lookup);
		$values = $this->_rets->GetLookupValues('Property', $fields[0]);
		foreach ($values AS $val)
		{
			$xml .= '<row>';
			$xml .= '<key> |' . htmlspecialchars($val['Value'])     . '</key>';
			$xml .= '<value>' . htmlspecialchars($val['LongValue']) . '</value>';
			$xml .= '</row>';
		}
		$xml .= '</rows>';

		$this->_send_xml(FALSE, $xml);
	}

	/**
	 * Output error in XML format
	 *
	 * Reads parameters and returns error XML string.
	 * @param string $type
	 * @param string $code
	 * @param string $text
	 * @param string $search
	 * @return string $xml
	 */
	private function _make_xml_error($type, $code, $text, $search)
	{
		$xml  = '<results><error>';
		$xml .= '<type>' . $type . '</type>';
		$xml .= '<code>' . $code . '</code>';
		$xml .= '<text>' . $text . '</text>';
		$xml .= '<query>' . $search . '</query>';
		$xml .= '</error></results>';

		return($xml);
	}

	/**
	 * Get list
	 *
	 * Gets list of properties and returns them in a XML string.
	 * @param string $string_out
	 * @return string
	 */
	public function get_list($string_out)
	{
		$this->_init_rets();

		CLogger::log('DEBUG', CCrypter::decrypt($_COOKIE['AD_RETS_U']), 'Attempting to perform search.');

		$search = '';
		if (null != $this->_rets)
		{
			$search = $this->_build_rets_query();
			$fields = array('Select' => AD_RETS_ALL_FLDS, 'Limit' => AD_RETS_SEARCH_LIMIT, 'Offset' => '0', 'StandardNames' => 0);

			$search_result = $this->_rets->SearchQuery('Property', AD_RETS_SEARCH_CLASS, $search, $fields);

			if (!$search_result)
			{
				$error = $this->_rets->Error();
				$x = $this->_make_xml_error($error['type'], $error['code'], $error['text'], $search);
				CLogger::log('DEBUG', CCrypter::decrypt($_COOKIE['AD_RETS_U']), 'Search failed, ' . $error['type'] . ' ' . $error['code'] . ' ' . $error['text']);
			}
			else //
			{
				$x  = '<results>';
				$x .= '<total>' . $this->_rets->TotalRecordsFound() . '</total>';
				$x .= '<rows>';

				$current_count = 0;
				while ($listing = $this->_rets->FetchRow($search_result))
				{
					$cur_row = '';
					$ml_number = '';
					foreach ($listing AS $name => $value)
					{
						$name = CUtils::get_standard_name(strtolower($name));

						if (FALSE !== strpos($name, 'date'))
						{
							$time_str = strtotime($value);

							if (FALSE !== $time_str)
							{
								$value = date('m/d/Y', $time_str);
							}
						}else if($name == 'ml_number')
						{
							$ml_number = $value;
						}
						
						$cur_row  .= '<' . $name . '>';
						$cur_row  .= htmlspecialchars($value);
						$cur_row  .= '</' . $name . '>';
					}
					if($ml_number != '')
					{					
						$x .= "<row ml_number='" . $ml_number . "' >" ;
					}
					else
					{
						$x .= '<row>';
					}
					$x .= $cur_row;
					$x .= '</row>';

					$current_count++;
				}
				$x .= '</rows>';
				$x .= '<current>' . $current_count . '</current>';
				$x .= '</results>';
				$this->_rets->FreeResult($search_result);

				CLogger::log('DEBUG', CCrypter::decrypt($_COOKIE['AD_RETS_U']), 'Search returned ' . $current_count . ' rows.');
			}
		}
		else
		{
			CLogger::log('DEBUG', CCrypter::decrypt($_COOKIE['AD_RETS_U']), 'Search failed due to failed RETS connection attempt.');
		}

		return $this->_send_xml($string_out, $x);
	}

	/**
	 * Returns the sites default blank image.
	 *
	 * Returns default blank image.
	 * @return array $img
	 */
	private function _get_blank_img()
	{
		$file = AD_SERVER_PATH . '/themes/default/imgs/no_image.jpg';

		$img[0]['Success'] = TRUE;
		$img[0]['Content-Type'] = mime_content_type($file);
		$img[0]['Length'] = filesize($file);

		$fh = fopen($file, 'r');
		$img[0]['Data'] = fread($fh, $img[0]['Length']);
		fclose($fh);

		return $img;
	}

	/**
	 * Print image
	 *
	 * Outputs image to screen.
	 * @param int $ml_number
	 * @param int $img_index
	 */
	private function _print_img($ml_number, $img_index)
	{
		$image = CUtils::mk_tmp_img($ml_number, $img_index);
		if (isset($image[0]))
		{
			$fh = fopen($image[0], 'r');
			$image_data = fread($fh, filesize($image[0]));
			fclose($fh);

			header('Content-type: ' . mime_content_type($image[0]));
			print_r($image_data);
		}
	}

	/**
	 * Gets image
	 *
	 * Gets and returns image data to caller, or null if not found.
	 * @param int $ml_number
	 * @param int $index
	 * @return array $img
	 */
	public function get_img($ml_number, $index = '*')
	{
		$this->_init_rets();

		CLogger::log('DEBUG', CCrypter::decrypt($_COOKIE['AD_RETS_U']), 'Attempting to get image.');

		$img_id = $ml_number;

		if ('ml_number' != $GLOBALS['MAP_RESULT_STD_NAME']['ad_img_id'])
		{
			$search = ('('.$GLOBALS['MAP_RESULT_STD_NAME']['ml_number'].'='.$ml_number.')');
			$search_result = $this->_rets->SearchQuery('Property', AD_RETS_SEARCH_CLASS, $search); //TODO check null
			if ($search_result)
			{
				$listing = $this->_rets->FetchRow($search_result);
				$img_id = $listing[$GLOBALS['MAP_RESULT_STD_NAME']['img_id']];
			}
		}
		$img = $this->_rets->GetObject('Property', 'Photo', $img_id, $index);

		if (is_array($img) && TRUE == $img[0]['Success'])
		{
			CLogger::log('DEBUG', CCrypter::decrypt($_COOKIE['AD_RETS_U']), 'Successfully received image.');
			return $img;
		}
		else
		{
			CLogger::log('DEBUG', CCrypter::decrypt($_COOKIE['AD_RETS_U']), 'Failed to get image, blank image displayed.');
			return $this->_get_blank_img();
		}
	}

	/**
	 * Output data
	 *
	 * Gets and returns XML is string format,
	 * or outputs XML to screen.
	 * @param string $string_out
	 * @param string $xml
	 * @return string
	 */
	private function _send_xml($string_out, & $xml)
	{
		if (TRUE == $string_out)
		{
			return($xml);
		}
		else //
		{
			CUtils::response_start_xml();
			echo($xml);
		}
	}

	/**
	 * Perform lookup
	 *
	 * Lookup property info and return it to caller.
	 * @param string $string_out
	 * @param string $field
	 * @return string
	 */
	private function _do_lookup($string_out, $field)
	{
		$x = '';
		$this->_init_rets();
		if (null != $this->_rets)
		{
			if (!$field)
			{
				$x = $this->_make_xml_error('Invalid arguments for the command.', 'Missing Arg', 'Missing Field', 'cmd=server&sub_cmd=lookup, field=city');
			}
			else //
			{
				$values = $this->_rets->GetLookupValues('Property', $GLOBALS['MAP_LOOKUP_FLD_NAME'][strtolower($field)]);

				if ($values)
				{
					$c = count($values);
					$x .= ('<results>');
					$x .= ('<total>'.$c.'</total>');
					$x .= ('<current>'.$c.'</current>');
					$x .= ('<rows>');
					$x .= ('<row><key>Any</key><value>Any</value></row>');

					if ($c != 0)
					{
						foreach ($values as $val)
						{
							$x .= sprintf("<row><key>%s</key><value>%s</value></row>", trim(htmlentities($val['Value'])), trim(htmlentities($val['LongValue'])));
						}
					}
					$x .= ('</rows>');
					$x .= ('</results>');
				}
				else //
				{
					$error = $this->_rets->Error();
					$x = $this->_make_xml_error($error['type'], $error['code'], $error['text'], $field);
				}
			}
			return $this->_send_xml($string_out, $x);
		}
	}

	/**
	 * Run test
	 *
	 * Run a test case.
	 */
	private function _do_test() //TODO
	{
		$this->_init_rets();

	 if (0)
		{
			$search = '((MLNumber=123456))';
			$search_result = $this->_rets->SearchQuery('Property', AD_RETS_SEARCH_CLASS, $search);
			$listing = $this->_rets->FetchRow($search_result);

			var_dump($listing);
		}
		else if (0)
		{
			$values = $this->_rets->GetLookupValues('Property', $GLOBALS['MAP_LOOKUP_FLD_NAME']['list_style']);
			$x = '';

			foreach ($values as $val)
			{
				$x .= sprintf("<a href=\"index.php?cmd=server&sub_cmd=search&city=%s\"> %s </a>", trim(htmlentities($val['Value'])), trim(htmlentities($val['Value'])));
				$x .= '<br>';
			}
			echo $x;
		}
		else if (1)
		{
			//$values = $this->_rets->GetMetadataObjects('Property');
			//$values = $this->_rets->GetMetadataObjects('Property', 'Photo');
			//$values = $this->_rets->GetAllLookupValues('Property');
			//$values = $this->_rets->GetLookupValues('Property', 'SPECIALSEARCHRES');
			$values = $this->_rets->GetMetadataTable('Property', AD_RETS_SEARCH_CLASS);
			//var_dump($values); die;

			echo '<pre>'; print_r($values); echo '</pre>'; die;

			foreach ($values AS $val)
			{
				var_dump($val);
				echo '<br><br><br>';
			}
		}
	}
}
